/*
 * @(#)TypeUtils.java 1.0 2008/12/07
 */
package stardust.core.java.reflect;


import java.lang.reflect.Array;
import stardust.core.support.conversion.TimeConversion;


/**
 * <code>TypeUtils</code> 类提供类型转换和基本数据间的转换的支持。
 * <p/>
 * @see MethodReflect
 * @see FieldReflect
 * @version 1.0, 2008/12/07
 * @author ChenChang
 */
public class TypeUtils
{

  /*
   * 存放所有基本数据的类数组，给判断基本数据类型的方法使用。
   */
  private static final Class<?>[] BASE_TYPE_CLASS =
  {
    byte.class, char.class, boolean.class, short.class, int.class, long.class, float.class, double.class,
    Byte.class, Character.class, Boolean.class, Short.class, Integer.class, Long.class, Float.class, Double.class,
    String.class
  };

  /**
   * <code>TypeUtils</code> 类不允许被实例化，它并没有被实例化的必要。
   */
  private TypeUtils()
  {
  }

  /**
   * <code>isBaseType</code> 方法判断参数是否为基本数据类，不支持数组类型。
   * <p/>
   * @param c
   * 需要判断的参数类型，它是一个 {@link Class} 类型。
   * <p/>
   * @return 如果是基本数据类型将返回 <tt>true</tt> 值。
   */
  public static boolean isBaseType(Class<?> c)
  {
    for (int i = 0; i < BASE_TYPE_CLASS.length; i++)
    {
      if (BASE_TYPE_CLASS[i] == c)
      {
        return true;
      }
    }

    return false;
  }

  public static Object caseArrayToType(Object obj, Class<?> c, Object... param) throws ClassCastException
  {
    Object array;

    if (obj == null)
    {
      return null;
    }

    if (c.isArray())
    {
      c = c.getComponentType();
    }

    if (!obj.getClass().isArray())
    {
      obj = new Object[]
      {
        obj
      };
    }

    if (obj.getClass().getComponentType().equals(c))
    {
      return obj;
    }

    array = Array.newInstance(c, Array.getLength(obj));

    for (int i = 0; i < Array.getLength(obj); i++)
    {
      Array.set(array, i, castToType(Array.get(obj, i), c, param));
    }

    return array;
  }

  /**
   * <code>castToType</code> 方法将指定参数转为指定类型，不支持数组类型。<br />
   * 如果参数为 <tt>null</tt> 值则返回 <tt>null</tt> 值，转换失败时抛出 {@link ClassCastException}
   * 异常。
   * <p/>
   * @param o 需要转换的对象，它可以是任何的类型。
   * @param c 期望转换成为的类型，它是一个 {@link Class} 类型。
   * @param param 在转换过程中需要使用的参数，例如对日期型进行转换时有必要指定格式化字符串。
   * <p/>
   * @throws ClassCastException 当转换失败时抛出此异常，传递 <tt>null</tt> 值作为参数不会引发此异常。
   * @return 转换成功返回转换后的类型，失败会引发 {@link ClassCastException} 异常。
   */
  public static Object castToType(Object o, Class<?> c, Object... param) throws ClassCastException
  {
    if (o == null)
    {
      return o;
    }

    if (o.getClass().isArray())
    {
      return caseArrayToType(o, c, param);
    }

    Class<?> oc = o.getClass();

    try
    {
      if (oc.asSubclass(c) != null)
      {
        return c.cast(o);
      }
    }
    catch(ClassCastException e)
    {
      ;
    }

    if (isBaseType(c))
    {
      if (o == null)
      {
        return null;
      }
      else if (c != String.class && o instanceof String && ((String) o).isEmpty())
      {
        return null;
      }
      else if (c == boolean.class || c == Boolean.class)
      {
        if (o instanceof Number)
        {
          return !(Integer.parseInt(o.toString()) == 0);
        }
        else
        {
          return Boolean.valueOf(o.toString());
        }
      }
      else if (c == byte.class || c == Byte.class)
      {
        return Byte.parseByte(o.toString());
      }
      else if (c == char.class || c == Character.class)
      {
        if (o instanceof Number && Long.parseLong(o.toString()) < 255)
        {
          return (char) (Byte.parseByte(o.toString()));
        }
        else if (o.toString().length() == 1)
        {
          return ((String) o).charAt(0);
        }
      }
      else if (c == short.class || c == Short.class)
      {
        return Short.parseShort(o.toString());
      }
      else if (c == int.class || c == Integer.class)
      {
        if (oc == char.class || oc == Character.class)
        {
          return ((int) ((Character) o).charValue());
        }
        else
        {
          return Integer.parseInt(o.toString());
        }
      }
      else if (c == long.class || c == Long.class)
      {
        return Long.parseLong(o.toString());
      }
      else if (c == float.class || c == Float.class)
      {
        return Float.parseFloat(o.toString());
      }
      else if (c == double.class || c == Double.class)
      {
        return Double.parseDouble(o.toString());
      }
      else if (c == String.class)
      {
        return o.toString();
      }
    }
    else if (c == java.util.Date.class || c == java.sql.Date.class || c == java.sql.Time.class || c == java.sql.Timestamp.class)
    {
      if (param != null && !o.toString().isEmpty() && param.length > 0)
      {
        try
        {
          java.util.Date date = TimeConversion.parseTime(o.toString(), param[0].toString());

          if (c == java.sql.Date.class)
          {
            return new java.sql.Date(date.getTime());
          }
          else if (c == java.sql.Time.class)
          {
            return new java.sql.Time(date.getTime());
          }
          else if (c == java.sql.Timestamp.class)
          {
            return new java.sql.Timestamp(date.getTime());
          }

          return date;
        }
        catch(Exception ex)
        {
          throw new ReflectException("无法将字符串类型[" + o.toString() + "]转换为日期类型[" + param[0] + "]。", ex);
        }
      }
      else
      {
        return null;
      }
    }

    throw new ClassCastException("can't cast " + o.getClass().getName() + " to " + c.getClass().getName());
  }

  /**
   * <code>testCastToType</code> 测试第一个参数类型是否可以转换到第二个参数类型。<br />
   * 如果允许转换将返回 <tt>true</tt> 值，但返回 <tt>true</tt> 值只是表示两个类型间数据可以兼容存放，并表示一定可以转换成功。
   * <p/>
   * @param oc
   * 源类型是一个 {@link Class} 类型。
   * @param c
   * 目标类型一个 {@link Class} 类型。
   * <p/>
   * @return 如果允许转换则返回 <tt>true</tt> 值。
   */
  public static Boolean testCastToType(Class<?> oc, Class<?> c)
  {
    if (oc == null || c == null)
    {
      return false;
    }

    try
    {
      if (oc.asSubclass(c) != null)
      {
        return true;
      }
    }
    catch(ClassCastException e)
    {
      ;
    }

    if (isBaseType(c))
    {
      return true;
    }
    else
    {
      return false;
    }
  }

  /**
   * 通过给定的数据类型字符串来获取类对象，如果它是基本数据类型将返回包装类型。
   * <p/>
   * @param type
   * 目标类型一个 {@link String} 类型。
   * <p/>
   * @return 如果是基本数据类型则返回它所对应的包装类型类，如果是其它类型则返回该类的类实例。
   */
  public static Class<?> getType(String type)
  {
    if (type.equals(java.lang.Character.class.getName()) || type.equals(char.class.getName()))
    {
      return java.lang.Character.class;
    }
    else if (type.equals(java.lang.String.class.getName()))
    {
      return java.lang.String.class;
    }
    else if (type.equals(java.lang.Byte.class.getName()) || type.equals(byte.class.getName()))
    {
      return java.lang.Byte.class;
    }
    else if (type.equals(java.lang.Short.class.getName()) || type.equals(short.class.getName()))
    {
      return java.lang.Short.class;
    }
    else if (type.equals(java.lang.Integer.class.getName()) || type.equals(int.class.getName()))
    {
      return java.lang.Integer.class;
    }
    else if (type.equals(java.lang.Long.class.getName()) || type.equals(long.class.getName()))
    {
      return java.lang.Long.class;
    }
    else if (type.equals(java.lang.Float.class.getName()) || type.equals(float.class.getName()))
    {
      return java.lang.Float.class;
    }
    else if (type.equals(java.lang.Double.class.getName()) || type.equals(double.class.getName()))
    {
      return java.lang.Double.class;
    }
    else if (type.equals(java.lang.Boolean.class.getName()) || type.equals(boolean.class.getName()))
    {
      return java.lang.Boolean.class;
    }

    try
    {
      return Class.forName(type);
    }
    catch(ClassNotFoundException e)
    {
      return null;
    }
  }

}
